From 4858ae47e212f9dc4e73bb8a6b929179e0ade0c5 Mon Sep 17 00:00:00 2001 From: Tristan Van Berkom Date: Thu, 6 Nov 2008 17:34:30 +0000 Subject: [PATCH] Made buildable and added support for adding children of type "submenu" * gtk/gtkmenuitem.c: Made buildable and added support for adding children of type "submenu" * gtk/gtkwindow.c: Added support for custom tag "accel-groups" to add GtkAccelGroups to the window. * gtk/gtkcontainer.c: Added builder contextual warnings in buildable_add_child() * gtk/tests/builder.c: Added tests for buildable menus (test that accelerators are properly connected on stock items, test the menu hierarchy, test permission to add alien/custom menuitem children). * docs/reference/gtk/tmpl/gtkbuilder.sgml, docs/reference/gtk/tmpl/gtkwindow.sgml, docs/reference/gtk/tmpl/gtkmenuitem.sgml: Updated docs for buildable submenus and accel groups. svn path=/trunk/; revision=21767 --- ChangeLog | 18 +++ docs/reference/gtk/tmpl/gtkbuilder.sgml | 2 + docs/reference/gtk/tmpl/gtkmenuitem.sgml | 18 +++ docs/reference/gtk/tmpl/gtkwindow.sgml | 23 ++++ gtk/gtkcontainer.c | 14 ++- gtk/gtkmenuitem.c | 33 ++++- gtk/gtkwindow.c | 125 +++++++++++++++++- gtk/tests/builder.c | 153 +++++++++++++++++++++++ 8 files changed, 380 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 54c5c1ad91..475677278c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2008-11-06 Tristan Van Berkom + + * gtk/gtkmenuitem.c: Made buildable and added support for adding children + of type "submenu" + + * gtk/gtkwindow.c: Added support for custom tag "accel-groups" to add GtkAccelGroups + to the window. + + * gtk/gtkcontainer.c: Added builder contextual warnings in buildable_add_child() + + * gtk/tests/builder.c: Added tests for buildable menus (test that accelerators are + properly connected on stock items, test the menu hierarchy, test permission to + add alien/custom menuitem children). + + * docs/reference/gtk/tmpl/gtkbuilder.sgml, docs/reference/gtk/tmpl/gtkwindow.sgml, + docs/reference/gtk/tmpl/gtkmenuitem.sgml: Updated docs for buildable submenus + and accel groups. + 2008-11-06 Tristan Van Berkom * gtk/gtkmenuitem.[ch]: added new apis gtk_menu_item_[set/get]_label() and diff --git a/docs/reference/gtk/tmpl/gtkbuilder.sgml b/docs/reference/gtk/tmpl/gtkbuilder.sgml index 21f5073f4d..57e18735d5 100644 --- a/docs/reference/gtk/tmpl/gtkbuilder.sgml +++ b/docs/reference/gtk/tmpl/gtkbuilder.sgml @@ -212,6 +212,7 @@ These XML fragments are explained in the documentation of the respective objects, see GtkWidget, GtkLabel, +GtkWindow, GtkContainer, GtkDialog, GtkCellLayout, @@ -227,6 +228,7 @@ respective objects, see GtkTreeView, GtkUIManager, GtkActionGroup. +GtkMenuItem. diff --git a/docs/reference/gtk/tmpl/gtkmenuitem.sgml b/docs/reference/gtk/tmpl/gtkmenuitem.sgml index a3e01835d3..acc28908e1 100644 --- a/docs/reference/gtk/tmpl/gtkmenuitem.sgml +++ b/docs/reference/gtk/tmpl/gtkmenuitem.sgml @@ -14,6 +14,24 @@ alignment, events and submenus. As it derives from #GtkBin it can hold any valid child widget, altough only a few are really useful. + +GtkMenuItem as GtkBuildable + +The GtkMenuItem implementation of the GtkBuildable interface +supports adding a submenu by specifying "submenu" as the "type" +attribute of a <child> element. + + +A UI definition fragment with submenus + + + + + +]]> + + diff --git a/docs/reference/gtk/tmpl/gtkwindow.sgml b/docs/reference/gtk/tmpl/gtkwindow.sgml index 03f4ac15d6..377a79b3af 100644 --- a/docs/reference/gtk/tmpl/gtkwindow.sgml +++ b/docs/reference/gtk/tmpl/gtkwindow.sgml @@ -8,6 +8,29 @@ Toplevel which can contain other widgets + +GtkWindow as GtkBuildable + +The GtkWindow implementation of the GtkBuildable interface supports a +custom <accel-groups> element, which supports any number of <group> +elements representing the GtkAccelGroup objects you want to add to your +window (synonymous with gtk_window_add_accel_group(). + + +A UI definition fragment with accel groups + + + + + + +... + + +]]> + + diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c index c01a373702..30eb06ff2b 100644 --- a/gtk/gtkcontainer.c +++ b/gtk/gtkcontainer.c @@ -307,9 +307,17 @@ gtk_container_buildable_add_child (GtkBuildable *buildable, GObject *child, const gchar *type) { - g_return_if_fail (GTK_IS_WIDGET (child)); - - gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); + if (type) + { + GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type); + } + else if (GTK_IS_WIDGET (child) && GTK_WIDGET_TOPLEVEL (child) == FALSE) + { + gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child)); + } + else + g_warning ("Cannot add an object of type %s to a container of type %s", + g_type_name (G_OBJECT_TYPE (child)), g_type_name (G_OBJECT_TYPE (buildable))); } static void diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c index 2ad13fc3a6..1d24a4e70d 100644 --- a/gtk/gtkmenuitem.c +++ b/gtk/gtkmenuitem.c @@ -36,6 +36,7 @@ #include "gtkmenubar.h" #include "gtkseparatormenuitem.h" #include "gtkprivate.h" +#include "gtkbuildable.h" #include "gtkintl.h" #include "gtkalias.h" @@ -113,9 +114,19 @@ static void gtk_real_menu_item_set_label (GtkMenuItem *menu_item, static G_CONST_RETURN gchar * gtk_real_menu_item_get_label (GtkMenuItem *menu_item); +static void gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface); +static void gtk_menu_item_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type); + static guint menu_item_signals[LAST_SIGNAL] = { 0 }; -G_DEFINE_TYPE (GtkMenuItem, gtk_menu_item, GTK_TYPE_ITEM) +static GtkBuildableIface *parent_buildable_iface; + +G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_ITEM, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_menu_item_buildable_interface_init)) static void gtk_menu_item_class_init (GtkMenuItemClass *klass) @@ -474,6 +485,26 @@ gtk_menu_item_detacher (GtkWidget *widget, menu_item->submenu = NULL; } +static void +gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->add_child = gtk_menu_item_buildable_add_child; +} + +static void +gtk_menu_item_buildable_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *type) +{ + if (type && strcmp (type, "submenu") == 0) + gtk_menu_item_set_submenu (GTK_MENU_ITEM (buildable), + GTK_WIDGET (child)); + else + parent_buildable_iface->add_child (buildable, builder, child, type); +} + /** * gtk_menu_item_set_submenu: * @menu_item: a #GtkMenuItem diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index f83b4513ad..1858b84111 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -310,6 +310,7 @@ static GQuark quark_gtk_embedded = 0; static GQuark quark_gtk_window_key_hash = 0; static GQuark quark_gtk_window_default_icon_pixmap = 0; static GQuark quark_gtk_window_icon_info = 0; +static GQuark quark_gtk_buildable_accels = 0; static GtkBuildableIface *parent_buildable_iface; @@ -330,6 +331,17 @@ static void gtk_window_buildable_set_buildable_property (GtkBuildable *bu const GValue *value); static void gtk_window_buildable_parser_finished (GtkBuildable *buildable, GtkBuilder *builder); +static gboolean gtk_window_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_window_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data); G_DEFINE_TYPE_WITH_CODE (GtkWindow, gtk_window, GTK_TYPE_BIN, @@ -415,6 +427,7 @@ gtk_window_class_init (GtkWindowClass *klass) quark_gtk_window_key_hash = g_quark_from_static_string ("gtk-window-key-hash"); quark_gtk_window_default_icon_pixmap = g_quark_from_static_string ("gtk-window-default-icon-pixmap"); quark_gtk_window_icon_info = g_quark_from_static_string ("gtk-window-icon-info"); + quark_gtk_buildable_accels = g_quark_from_static_string ("gtk-window-buildable-accels"); gobject_class->dispose = gtk_window_dispose; gobject_class->finalize = gtk_window_finalize; @@ -1137,7 +1150,8 @@ gtk_window_buildable_interface_init (GtkBuildableIface *iface) parent_buildable_iface = g_type_interface_peek_parent (iface); iface->set_buildable_property = gtk_window_buildable_set_buildable_property; iface->parser_finished = gtk_window_buildable_parser_finished; - + iface->custom_tag_start = gtk_window_buildable_custom_tag_start; + iface->custom_finished = gtk_window_buildable_custom_finished; } static void @@ -1159,11 +1173,118 @@ gtk_window_buildable_parser_finished (GtkBuildable *buildable, GtkBuilder *builder) { GtkWindowPrivate *priv = GTK_WINDOW_GET_PRIVATE (buildable); + GObject *object; + GSList *accels, *l; if (priv->builder_visible) gtk_widget_show (GTK_WIDGET (buildable)); - parent_buildable_iface->parser_finished (buildable, builder); + accels = g_object_get_qdata (G_OBJECT (buildable), quark_gtk_buildable_accels); + for (l = accels; l; l = l->next) + { + object = gtk_builder_get_object (builder, l->data); + if (!object) + { + g_warning ("Unknown accel group %s specified in window %s", + (const gchar*)l->data, gtk_buildable_get_name (buildable)); + continue; + } + gtk_window_add_accel_group (GTK_WINDOW (buildable), + GTK_ACCEL_GROUP (object)); + g_free (l->data); + } + + g_object_set_qdata (G_OBJECT (buildable), quark_gtk_buildable_accels, NULL); + + parent_buildable_iface->parser_finished (buildable, builder); +} + +typedef struct { + GObject *object; + GSList *items; +} GSListSubParserData; + +static void +window_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + guint i; + GSListSubParserData *data = (GSListSubParserData*)user_data; + + if (strcmp (element_name, "group") == 0) + { + for (i = 0; names[i]; i++) + { + if (strcmp (names[i], "name") == 0) + data->items = g_slist_prepend (data->items, g_strdup (values[i])); + } + } + else if (strcmp (element_name, "accel-groups") == 0) + return; + else + g_warning ("Unsupported tag type for GtkWindow: %s\n", + element_name); + +} + +static const GMarkupParser window_parser = + { + window_start_element + }; + +static gboolean +gtk_window_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + GSListSubParserData *parser_data; + + if (parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data)) + return TRUE; + + if (strcmp (tagname, "accel-groups") == 0) + { + parser_data = g_slice_new0 (GSListSubParserData); + parser_data->items = NULL; + parser_data->object = G_OBJECT (buildable); + + *parser = window_parser; + *data = parser_data; + return TRUE; + } + + return FALSE; +} + +static void +gtk_window_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data) +{ + GSListSubParserData *data; + + parent_buildable_iface->custom_finished (buildable, builder, child, + tagname, user_data); + + if (strcmp (tagname, "accel-groups") != 0) + return; + + data = (GSListSubParserData*)user_data; + + g_object_set_qdata_full (G_OBJECT (buildable), quark_gtk_buildable_accels, + data->items, (GDestroyNotify) g_slist_free); + + g_slice_free (GSListSubParserData, data); } /** diff --git a/gtk/tests/builder.c b/gtk/tests/builder.c index 8415666ebc..d8b87d2711 100644 --- a/gtk/tests/builder.c +++ b/gtk/tests/builder.c @@ -2282,6 +2282,158 @@ test_add_objects (void) g_object_unref (builder); } +GtkWidget * +get_parent_menubar (GtkWidget *menuitem) +{ + GtkMenuShell *menu_shell = (GtkMenuShell *)menuitem->parent; + GtkWidget *attach = NULL; + + g_assert (GTK_IS_MENU_SHELL (menu_shell)); + + while (menu_shell && !GTK_IS_MENU_BAR (menu_shell)) + { + if (GTK_IS_MENU (menu_shell) && + (attach = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))) != NULL) + menu_shell = (GtkMenuShell *)attach->parent; + else + menu_shell = NULL; + } + + return menu_shell ? GTK_WIDGET (menu_shell) : NULL; +} + +static void +test_menus (void) +{ + gchar *buffer = + "" + " " + " " + " " + " " + " " + " " + " True" + " vertical" + " " + " " + " True" + " " + " " + " True" + " _File" + " True" + " " + " " + " True" + " " + " " + " gtk-new" + " True" + " True" + " accelgroup1" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + + gchar *buffer1 = + "" + " " + " " + " " + " " + " " + " " + " True" + " vertical" + " " + " " + " True" + " " + " " + " True" + " " + " " + " True" + " a label" + " " + " " + " " + " " + " " + " " + " " + " " + " " + "" + ""; + GtkBuilder *builder; + GtkWidget *window, *item; + GtkAccelGroup *accel_group; + GtkWidget *item_accel_label, *sample_accel_label, *sample_menu_item, *custom; + + /* Check that the item has the correct accel label string set + */ + builder = builder_new_from_string (buffer, -1, NULL); + window = (GtkWidget *)gtk_builder_get_object (builder, "window1"); + item = (GtkWidget *)gtk_builder_get_object (builder, "imagemenuitem1"); + accel_group = (GtkAccelGroup *)gtk_builder_get_object (builder, "accelgroup1"); + + gtk_widget_show_all (window); + + sample_menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_NEW, accel_group); + + g_assert (GTK_BIN (sample_menu_item)->child); + g_assert (GTK_IS_ACCEL_LABEL (GTK_BIN (sample_menu_item)->child)); + sample_accel_label = GTK_WIDGET (GTK_BIN (sample_menu_item)->child); + gtk_widget_show (sample_accel_label); + + g_assert (GTK_BIN (item)->child); + g_assert (GTK_IS_ACCEL_LABEL (GTK_BIN (item)->child)); + item_accel_label = GTK_WIDGET (GTK_BIN (item)->child); + + gtk_accel_label_refetch (GTK_ACCEL_LABEL (sample_accel_label)); + gtk_accel_label_refetch (GTK_ACCEL_LABEL (item_accel_label)); + + g_assert (GTK_ACCEL_LABEL (sample_accel_label)->accel_string != NULL); + g_assert (GTK_ACCEL_LABEL (item_accel_label)->accel_string != NULL); + g_assert (strcmp (GTK_ACCEL_LABEL (item_accel_label)->accel_string, + GTK_ACCEL_LABEL (sample_accel_label)->accel_string) == 0); + + /* Check the menu heirarchy worked here */ + g_assert (get_parent_menubar (item)); + + gtk_widget_destroy (GTK_WIDGET (window)); + gtk_widget_destroy (sample_menu_item); + g_object_unref (builder); + + + /* Check that we can add alien children to menu items via normal + * GtkContainer apis. + */ + builder = builder_new_from_string (buffer1, -1, NULL); + window = (GtkWidget *)gtk_builder_get_object (builder, "window1"); + item = (GtkWidget *)gtk_builder_get_object (builder, "imagemenuitem1"); + custom = (GtkWidget *)gtk_builder_get_object (builder, "custom1"); + + g_assert (custom->parent == item); + + gtk_widget_destroy (GTK_WIDGET (window)); + g_object_unref (builder); + +} + + static void test_file (const gchar *filename) { @@ -2367,6 +2519,7 @@ main (int argc, char **argv) g_test_add_func ("/Builder/PangoAttributes", test_pango_attributes); g_test_add_func ("/Builder/Requires", test_requires); g_test_add_func ("/Builder/AddObjects", test_add_objects); + g_test_add_func ("/Builder/Menus", test_menus); return g_test_run(); } -- 2.30.2